package com.apobates.jforum2.troll.nest.controller;

import com.apobates.jforum2.threads.stub.TopicCommonResponseVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsAlbumPictureRecordVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsAlbumPictureVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsAlbumVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsCategoryVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsCollectStatsVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsConfigVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsStatsVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsTagRelateVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsTagVo;
import com.apobates.jforum2.threads.stub.vo.ThreadsVo;
import com.apobates.jforum2.troll.forum.stub.vo.BoardGroupVo;
import com.apobates.jforum2.troll.forum.stub.vo.BoardVo;
import com.apobates.jforum2.troll.nest.service.BoardClient;
import com.apobates.jforum2.troll.nest.service.BoardGroupClient;
import com.apobates.jforum2.troll.nest.service.MemberClient;
import com.apobates.jforum2.troll.nest.service.PostsClient;
import com.apobates.jforum2.troll.nest.service.TopicClient;
import com.apobates.jforum2.troll.nest.vo.*;
import com.apobates.jforum2.troll.replies.stub.vo.ReplyVo;
import com.apobates.jforum2.troll.utils.core.Commons;
import com.apobates.jforum2.troll.utils.core.DateTimeUtils;
import com.apobates.jforum2.troll.utils.core.EntityPageResult;
import com.apobates.jforum2.troll.utils.core.ResponseResult;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

/**
 * 话题服务API控制器
 * 注意: 需要带有服务响应的状态码使用ResponseEntity, 对于返回值对象的请求在失败时返回404,对于String响应的操作返回401
 * @author xiaofanku
 * @since 20210412
 */
@RestController
@RequestMapping("/api/threads")
public class TopicClientController {
    @Autowired
    private TopicClient topicClient;
    @Autowired
    private PostsClient postsClient;
    @Autowired
    private BoardClient boardClient;
    @Autowired
    private BoardGroupClient boardGroupClient;
    @Autowired
    private MemberClient memberClient;
    private final static Logger logger = LoggerFactory.getLogger(TopicClientController.class);

    //1:查看指定的话题
    @GetMapping("/{id}")
    public ThreadsVo get(@PathVariable("id") long topicId){
        ResponseEntity<ThreadsVo> mv = topicClient.get(topicId);
        return Optional.ofNullable(mv.getBody()).orElse(new ThreadsVo());
    }
    
    //3:查看指定话题并级联加载版块组(卷),版块
    @GetMapping("/{id}/fold/{board}/{volume}")
    public ResponseEntity<ThreadsFoldVo> getCascade(@PathVariable("id") long topicId, @PathVariable("board")long boardId, @PathVariable("volume")int boardGroupId){
        CompletableFuture<BoardVo> bv = CompletableFuture.supplyAsync(()->boardClient.get(boardId).getBody()).completeOnTimeout(null, 1, TimeUnit.SECONDS);
        CompletableFuture<ThreadsVo> tv = CompletableFuture.supplyAsync(()->topicClient.get(topicId).getBody()).completeOnTimeout(null, 1, TimeUnit.SECONDS);
        CompletableFuture<BoardGroupVo> bgv = CompletableFuture.supplyAsync(()->boardGroupClient.get(boardGroupId).getBody()).completeOnTimeout(null, 1, TimeUnit.SECONDS);
        //
        ThreadsFoldVo rs = bv.thenCombine(tv, (bvObj, tvObj)->{
            if(null == tvObj){
                return null;
            }
            ThreadsFoldVo tmp = new ThreadsFoldVo();
            tmp.setThreadsVo(tvObj);
            tmp.setBoardVo(bvObj);
            return tmp;
        }).thenCompose(midObj->{
            return bgv.thenApply(bgvObj -> {
                ThreadsFoldVo data = new ThreadsFoldVo();
                data.setThreadsVo(midObj.getThreadsVo());
                data.setBoardVo(midObj.getBoardVo());
                data.setVolumesVo(Optional.ofNullable(bgvObj).orElse(BoardGroupVo.defaultInstance()));
                return data;
            });
        }).completeOnTimeout(null, 1, TimeUnit.SECONDS).join();
        //
        ResponseResult<ThreadsFoldVo> rr = ResponseResult.Helper.ofEntity(rs);
        return ResponseEntity.status(rr.getCode()).contentType(MediaType.APPLICATION_JSON).body(rr.getEntity());
    }
    
    //4:查看指定版块下的指定类型的话题,不包括删除;级联加载话题统计
    @GetMapping("/board/{board}/{category}/{page}/{number}")
    public EntityPageResult<ThreadsCollectStatsVo> getAll(
            @PathVariable("board")long boardId,
            @PathVariable(value="category", required=true)String categoryValue,
            @PathVariable("page")int page,
            @PathVariable("number")int pageSize){
        return topicClient.getAll(boardId, categoryValue, page, pageSize);
    }
    
    //5:查看指定版块组最近发布的话题,方法将级联加载话题统计
    @GetMapping("/volume/{volume}/recent")
    public List<ThreadsCollectStatsVo> getRecent(@PathVariable("volume") int boardGroupId, @RequestParam("size") int showSize){
        return topicClient.getRecent(boardGroupId, showSize);
    }
    
    //6:查看指定版块组(卷)下的最多回复的话题
    @GetMapping("/volume/{volume}/maxreply")
    public List<ThreadsVo> getMaxReplyForBoardGroup(@PathVariable("volume") int boardGroupId, @RequestParam("size")int showSize){
        return topicClient.getMaxReplyForBoardGroup(boardGroupId, showSize);
    }
    
    //7:查看指定版块组(卷)下的精华话题
    @GetMapping("/volume/{volume}/good")
    public List<ThreadsVo> getGoodsForBoardGroup(@PathVariable("volume") int boardGroupId, @RequestParam("size") int showSize){
        return topicClient.getGoodsForBoardGroup(boardGroupId, showSize);
    }
    
    //8:查看回复最多的话题
    @GetMapping("/stats/maxreply")
    public List<ThreadsVo> getHot(@RequestParam("size")int showSize){
        return topicClient.getHot(showSize);
    }
    
    //9:指定版块置顶的话题,不包括删除;会级联加载话题统计,最多显示5条
    @GetMapping("/board/{board}/top")
    public List<ThreadsCollectStatsVo> getTopForBoard(@PathVariable("board") long boardId){
        return topicClient.getTopForBoard(boardId);
    }
    
    //10:自参考日期以后版块最近的话题,会级联加载话题统计
    @GetMapping("/board/{board}/recent/{stamp}")
    public List<ThreadsCollectStatsVo> getRecentByUnixStamp(@PathVariable("board")long boardId, @PathVariable("stamp")int previousUx){
        return topicClient.getRecentByUnixStamp(boardId, previousUx);
    }
    
    //11:自参考日期以后版块最近的话题数量
    @GetMapping("/board/{board}/recent/{stamp}/size")
    public long getRecentByUnixStampSize(@PathVariable("board")long boardId, @PathVariable("stamp")int previousUx){
        return topicClient.getRecentByUnixStampSize(boardId, previousUx);
    }
    
    //12:查看最近发布的话题,不包括删除的,不包含举报和反馈类型的.方法会级联加载版块,话题统计
    @GetMapping("/home/recent")
    public List<ThreadsCollectStatsAndBoardVo> getRecentRelateContentCase(@RequestParam("size") int showSize){
        List<ThreadsCollectStatsVo> rs = topicClient.getRecentRelateContent(showSize);
        //版块ID
        Set<Long> boardIds = rs.stream().map(tcs->tcs.getThreadsVo().getBoard()).collect(Collectors.toSet());
        Map<Long,BoardVo> boardsMap = boardClient.getMultiBoard(Commons.longToString(boardIds)).stream().collect(Collectors.toMap(BoardVo::getId, Function.identity()));
        return rs.stream().map(tv->{
            ThreadsCollectStatsAndBoardVo tmp = new ThreadsCollectStatsAndBoardVo();
            tmp.setThreadsVo(tv.getThreadsVo());
            tmp.setStatsVo(tv.getStatsVo());
            tmp.setBoardVo(boardsMap.get(tv.getThreadsVo().getBoard()));
            return tmp;
        }).collect(Collectors.toList());
    }
    
    //13:查看指定的话题并级联加载话题的内容,话题统计,会员信息(前端话题内容页)
    @GetMapping("/{id}/content")
    public ResponseEntity<ThreadsCollectStatsAndReplyVo> getContentCase(@PathVariable("id")long topicId){
        ThreadsCollectStatsAndReplyVo t1 = wrapThreadsCollectStatsAndReply(()->topicClient.getCaseStats(topicId).getBody(), ()->postsClient.getOneFloorContent(topicId).getBody());
        //
        ResponseResult<ThreadsCollectStatsAndReplyVo> rr = ResponseResult.Helper.ofEntity(t1);
        return ResponseEntity.status(rr.getCode()).contentType(MediaType.APPLICATION_JSON).body(rr.getEntity());
    }
    
    //14:查看指定的话题并级联加载话题的内容,话题统计,会员信息(Mobile话题内容页)
    @GetMapping("/{id}/content/{scale}")
    public ResponseEntity<ThreadsCollectStatsAndReplyVo> getDecodeContentCase(@PathVariable("id")long topicId, @RequestParam(value="scale", required=false, defaultValue="auto") String imageScale){
        ThreadsCollectStatsAndReplyVo t1 = wrapThreadsCollectStatsAndReply(()->topicClient.getCaseStats(topicId).getBody(), ()->postsClient.getOneFloorDecodeContent(topicId, imageScale).getBody());
        //
        ResponseResult<ThreadsCollectStatsAndReplyVo> rr = ResponseResult.Helper.ofEntity(t1);
        return ResponseEntity.status(rr.getCode()).contentType(MediaType.APPLICATION_JSON).body(rr.getEntity());
    }
    
    private ThreadsCollectStatsAndReplyVo wrapThreadsCollectStatsAndReply(Supplier<ThreadsCollectStatsVo> tcsv, Supplier<ReplyVo> tv){
        CompletableFuture<ThreadsCollectStatsVo> t1 = CompletableFuture.supplyAsync(tcsv).completeOnTimeout(null, 1, TimeUnit.SECONDS);
        CompletableFuture<ReplyVo> t2 = CompletableFuture.supplyAsync(tv).completeOnTimeout(null, 1, TimeUnit.SECONDS);
        
        return t1.thenCombine(t2, (tcsvStms, rvStms)->{
            ThreadsCollectStatsAndReplyVo tmp = new ThreadsCollectStatsAndReplyVo();
            tmp.setThreadsVo(tcsvStms.getThreadsVo());
            tmp.setStatsVo(tcsvStms.getStatsVo());
            tmp.setReplyVo(rvStms);
            return tmp;
        }).thenCompose(midObj->{
            return CompletableFuture.supplyAsync(()->memberClient.get(midObj.getThreadsVo().getMember())).thenApply(rs->{
                ThreadsCollectStatsAndReplyVo tmp1 = new ThreadsCollectStatsAndReplyVo();
                tmp1.setThreadsVo(midObj.getThreadsVo());
                tmp1.setStatsVo(midObj.getStatsVo());
                tmp1.setReplyVo(midObj.getReplyVo());
                tmp1.setMemberVo(rs.getBody());
                return tmp1;
            });
        }).completeOnTimeout(null, 1, TimeUnit.SECONDS).join();
    }
    
    //15:查看指定会员发布的所有话题,会级联加载话题统计
    @GetMapping("/member/recent/{member}/{page}/{number}")
    public EntityPageResult<ThreadsCollectStatsVo> getAllForMember(@PathVariable("member") long memberId, @PathVariable("page") int page, @PathVariable("number") int pageSize){
        return topicClient.getAllForMember(memberId, page, pageSize);
    }
    
    //15.1:查看会员回复的话题,话题的作者不是该会员(不包含自已发的),会级联加载话题的统计
    // (/member/reply/{member}/{page}/{number})
    
    //16:查看指定会员最近回复的话题,话题的作者不是该会员(不包含自已发的),级联加载版块
    @GetMapping("/member/reply/{member}")
    public List<ThreadsCollectBoardVo> getRecentReplyForMemberCaseBoard(@PathVariable("member") long memberId, @RequestParam("size") int showSize){
        List<ThreadsVo> rs = topicClient.getRecentReplyForMember(memberId, showSize);
        Set<Long> boardIds = rs.stream().map(ThreadsVo::getBoard).collect(Collectors.toSet());
        Map<Long,BoardVo> boardsMap = boardClient.getMultiBoard(Commons.longToString(boardIds)).stream().collect(Collectors.toMap(BoardVo::getId, Function.identity()));
        return rs.stream().map(tv->{
            ThreadsCollectBoardVo tmp = new ThreadsCollectBoardVo();
            tmp.setThreadsVo(tv);
            tmp.setBoardVo(boardsMap.get(tv.getBoard()));
            return tmp;
        }).collect(Collectors.toList());
    }
    //17:查看指定会员最近发布的话题并级联加载版块
    @GetMapping("/member/recent/{member}")
    public List<ThreadsCollectBoardVo> getRecentForMemberCaseBoard(@PathVariable("member") long memberId, @RequestParam("size") int showSize){
        List<ThreadsVo> rs = topicClient.getRecentForMember(memberId, showSize);
        Set<Long> boardIds = rs.stream().map(ThreadsVo::getBoard).collect(Collectors.toSet());
        Map<Long,BoardVo> boardsMap = boardClient.getMultiBoard(Commons.longToString(boardIds)).stream().collect(Collectors.toMap(BoardVo::getId, Function.identity()));
        return rs.stream().map(tv->{
            ThreadsCollectBoardVo tmp = new ThreadsCollectBoardVo();
            tmp.setThreadsVo(tv);
            tmp.setBoardVo(boardsMap.get(tv.getBoard()));
            return tmp;
        }).collect(Collectors.toList());
    }
    
    //18:查看会员发布的受欢迎的话题列表并级联加载话题统计,版块
    @GetMapping("/member/popular/{member}")
    public List<ThreadsCollectStatsAndBoardVo> getAllForMemberPopularCaseBoard(@PathVariable("member") long memberId, @RequestParam("size") int showSize){
        List<ThreadsCollectStatsVo> rs = topicClient.getAllForMemberPopular(memberId, showSize);
        Set<Long> boardIds = rs.stream().map(tcs->tcs.getThreadsVo().getBoard()).collect(Collectors.toSet());
        Map<Long,BoardVo> boardsMap = boardClient.getMultiBoard(Commons.longToString(boardIds)).stream().collect(Collectors.toMap(BoardVo::getId, Function.identity()));
        return rs.stream().map(tcsv->{
            ThreadsCollectStatsAndBoardVo tmp = new ThreadsCollectStatsAndBoardVo();
            tmp.setThreadsVo(tcsv.getThreadsVo());
            tmp.setStatsVo(tcsv.getStatsVo());
            tmp.setBoardVo(boardsMap.get(tcsv.getThreadsVo().getBoard()));
            return tmp;
        }).collect(Collectors.toList());
    }
    
    //19:查看指定版块最近的话题,不包括删除,置顶;包括加精,普通;以rankingDateTime倒序 服务于RSS
    @GetMapping("/rss/recent/board/{board}")
    public List<ThreadsVo> getRecentForBoard(@PathVariable("board") long boardId, @RequestParam("size") int showSize){
        return topicClient.getRecentForBoard(boardId, showSize);
    }
    
    //20:查看最近发布的话题,不包括删除的,不包含1号(举报和反馈所在)版块的
    //RSS
    @GetMapping("/rss/recent")
    public List<ThreadsVo> getRecent(@RequestParam("size") int showSize){
        return topicClient.getRecent(showSize);
    }
    
    //21:根据话题查看话题配置
    @GetMapping("/{id}/config")
    public ThreadsConfigVo getConfig(@PathVariable("id") long topicId){
        ResponseEntity<ThreadsConfigVo> rs = topicClient.getConfig(topicId);
        return Optional.ofNullable(rs.getBody()).orElse(new ThreadsConfigVo());
    }
    
    //22:查看话题的统计
    @GetMapping("/{id}/stats")
    public ThreadsStatsVo getStats(@PathVariable("id") long topicId){
        ResponseEntity<ThreadsStatsVo> rs = topicClient.getStats(topicId);
        return Optional.ofNullable(rs.getBody()).orElse(new ThreadsStatsVo());
    }
    
    //23查看指定话题的封面
    @GetMapping("/{id}/cover/{scale}")
    public String getCover(@PathVariable("id") long topicId, @PathVariable("scale") String scale){
        ResponseEntity<String> rs = topicClient.getCover(topicId, scale);
        return Optional.ofNullable(rs.getBody()).orElse("404");
    }
    
    //24:是否可以收藏, 可以返回true
    @GetMapping("/{id}/favorite/member/{member}")
    public boolean isFavorite(@PathVariable("id") long topicId, @PathVariable("member") long memberId){
        return topicClient.isFavorite(topicId, memberId);
    }
    
    //25:是否可以点赞, 可以返回true
    @GetMapping("/{id}/like/member/{member}")
    public boolean isLike(@PathVariable("id") long topicId, @PathVariable("member") long memberId){
        return topicClient.isLike(topicId, memberId);
    }

    //26:联合检查:
    // 1)是否可以收藏, 可以返回true
    // 2)是否可以点赞, 可以返回true
    // 使用SSE获取指定会员对指定话题可进行的操作
    // Angular采用的
    @GetMapping(value = "/{id}/check/{member}/operate", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter getThreadsUnionResult(@PathVariable("id") long topicId, @PathVariable("member") long memberId) {
        SseEmitter emitter = new SseEmitter(12000L);
        try {
            SseEmitter.SseEventBuilder event = SseEmitter.event()
                    .data(queryTopicUnionOperate(topicId, memberId))
                    .id(DateTimeUtils.getUnixTimestamp()+""+Commons.randomNumeric(4))
                    .name("ThreadsUnionCheck");
            emitter.send(event);
        } catch (Exception ex) {
            emitter.completeWithError(ex);
        } finally {
            emitter.complete();
        }
        return emitter;
    }
    
    //27:举报话题
    @PostMapping(value="/{id}/report", consumes="application/x-www-form-urlencoded")
    public ResponseEntity<ThreadsVo> report(@PathVariable("id") long topicId,
                     @RequestParam("content") String reportReason,
                     @RequestParam("type") int reportType,
                     @RequestParam("member") long memberId,
                     @RequestParam("nickname") String memberNickname,
                     @RequestParam("token") String token,
                     @RequestParam("ip") String ipAddr){
        try {
            ReplyVo oneFloor = postsClient.getOneFloorContent(topicId).getBody();
            if (null == oneFloor) {
                return ResponseEntity.badRequest().body(new ThreadsVo()); //("回复不存在或暂时无法访问");
            }

            return topicClient.report(topicId, reportReason, reportType, oneFloor.getFloor(), oneFloor.getNickname(), oneFloor.getLink(), oneFloor.getContent(), memberId, memberNickname, token, ipAddr);
        }catch (Exception e){
            logger.error("[nest-topic-report]ResponseEntity exception:"+e.getMessage());
        }
        return ResponseEntity.notFound().build();
    }
    
    //28:反馈
    @PostMapping(value="/feedback", consumes="application/x-www-form-urlencoded")
    public ResponseEntity<ThreadsVo> feedback(@RequestParam("title") String title,
                  @RequestParam("content") String content,
                  @RequestParam("member") long memberId,
                  @RequestParam("nickname") String memberNickname,
                  @RequestParam("token") String token,
                  @RequestParam("ip") String ipAddr){
        try {
            return topicClient.feedback(title, content, memberId, memberNickname, token, ipAddr);
        }catch (Exception e){
            logger.error("[nest-feedback]ResponseEntity exception:"+e.getMessage());
        }
        return ResponseEntity.notFound().build();
    }
    
    //29:移动话题
    @PutMapping(value="/{id}/move", consumes="application/x-www-form-urlencoded")
    public Map<String,String> move(@PathVariable("id") long topicId,
                      @RequestParam("board") long srcBoardId,
                      @RequestParam("target") long targetBoardId,
                      @RequestParam("targetBoardGroup") int targetBoardGroupId,
                      @RequestParam("member") long memberId,
                      @RequestParam("nickname") String memberNickname,
                      @RequestParam("token") String token,
                      @RequestParam("ip") String ipAddr){
        //可能是0(失败)1(成功)
        String topicAffect = topicClient.move(topicId, srcBoardId, targetBoardId, targetBoardGroupId, memberId, memberNickname, token, ipAddr).getBody();
        return Map.of("result", topicAffect, "args", topicId+"-"+targetBoardId+"-"+targetBoardGroupId);
    }
    
    //30:创建话题
    @PostMapping(value="/create", consumes="application/x-www-form-urlencoded")
    public ResponseEntity<ThreadsVo> create(@RequestParam("volume") int volumesId,
                           @RequestParam("board") long boardId,
                           @RequestParam("category") int category,
                           @RequestParam("title") String title,
                           @RequestParam("content") String content,
                           @RequestParam("member") long memberId,
                           @RequestParam("nickname") String memberNickname,
                           @RequestParam("anony") String isAnonyStr,
                           @RequestParam("token") String token,
                           @RequestParam("ip") String ipAddr){
        try {
            return topicClient.create(volumesId, boardId, category, title, content, memberId, memberNickname, isAnonyStr, token, ipAddr);
        }catch (Exception e){
            logger.error("[nest-topic-publish]ResponseEntity exception:"+e.getMessage());
        }
        return ResponseEntity.notFound().build();
    }
    
    /**
     * 31:查看指定版块最近的话题, 忽略状态;以entryDateTime倒序 服务于策略检查
     * @param boardId 版块ID
     * @param showSize 显示的数量
     * @return
     */
    @GetMapping("/board/{board}/recent")
    public List<ThreadsVo> getRecentForBoardIgnoreStatus(@PathVariable("board") long boardId, @RequestParam("size") int showSize){
        return topicClient.getRecentForBoardIgnoreStatus(boardId, showSize);
    }
    
    //32:查看版块话题可用的话题类型列表. 注意版块必须与版块组(卷)是包含关系
    @GetMapping("/category/{board}/{volume}")
    public List<ThreadsCategoryVo> getAllByBoardTopicCategories(@PathVariable("board")long boardId, @PathVariable("volume") int boardGroupId){
        return topicClient.getAllByBoardTopicCategories(boardId, boardGroupId);
    }
    
    //33:置顶话题
    @PutMapping(value="/{id}/top", consumes="application/x-www-form-urlencoded")
    public boolean toped(@PathVariable("id") long topicId,
              @RequestParam("member") long memberId,
              @RequestParam("nickname") String memberNickname,
              @RequestParam("token") String token,
              @RequestParam("ip") String ipAddr){
        return topicClient.toped(topicId, memberId, memberNickname, token, ipAddr);
    }
    
    //34:精华话题
    @PutMapping(value="/{id}/good", consumes="application/x-www-form-urlencoded")
    public boolean gooded(@PathVariable("id") long topicId,
               @RequestParam("member") long memberId,
               @RequestParam("nickname") String memberNickname,
               @RequestParam("token") String token,
               @RequestParam("ip") String ipAddr){
        return topicClient.gooded(topicId, memberId, memberNickname, token, ipAddr);
    }
    
    //35:锁定话题
    @PutMapping(value="/{id}/lock", consumes="application/x-www-form-urlencoded")
    public boolean locked(@PathVariable("id") long topicId,
               @RequestParam("member") long memberId,
               @RequestParam("nickname") String memberNickname,
               @RequestParam("token") String token,
               @RequestParam("ip") String ipAddr){
        return topicClient.locked(topicId, memberId, memberNickname, token, ipAddr);
    }
    
    //36:取消锁定
    @PutMapping(value="/{id}/lock/cancel", consumes="application/x-www-form-urlencoded")
    public boolean unLocked(@PathVariable("id") long topicId,
                @RequestParam("member") long memberId,
                @RequestParam("nickname") String memberNickname,
                @RequestParam("token") String token,
                @RequestParam("ip") String ipAddr){
        return topicClient.unLiked(topicId, memberId, memberNickname, token, ipAddr);
    }
    
    
    //37:删除话题
    @PutMapping(value="/{id}/remove", consumes="application/x-www-form-urlencoded")
    public boolean dele(@PathVariable("id") long topicId,
              @RequestParam("member") long memberId,
              @RequestParam("nickname") String memberNickname,
              @RequestParam("token") String token,
              @RequestParam("ip") String ipAddr){
        return topicClient.dele(topicId, memberId, memberNickname, token, ipAddr);
    }
    
    //38:收藏话题
    @PutMapping(value="/{id}/favorite", consumes="application/x-www-form-urlencoded")
    public boolean favorited(@PathVariable("id") long topicId,
                 @RequestParam("member") long memberId,
                 @RequestParam("nickname") String memberNickname,
                 @RequestParam("token") String token,
                 @RequestParam("ip") String ipAddr){
        return topicClient.favorited(topicId, memberId, memberNickname, token, ipAddr);
    }
    
    //39:取消收藏
    @PutMapping(value="/{id}/favorite/cancel", consumes="application/x-www-form-urlencoded")
    public boolean cancelFavorited(@PathVariable("id") long topicId,
                     @RequestParam("member") long memberId,
                     @RequestParam("nickname") String memberNickname,
                     @RequestParam("token") String token,
                     @RequestParam("ip") String ipAddr){
        return topicClient.cancelFavorited(topicId, memberId, memberNickname, token, ipAddr);
    }
    
    //40:话题点赞
    @PutMapping(value="/{id}/like", consumes="application/x-www-form-urlencoded")
    public boolean liked(@PathVariable("id") long topicId,
               @RequestParam("member") long memberId,
               @RequestParam("nickname") String memberNickname,
               @RequestParam("token") String token,
               @RequestParam("ip") String ipAddr){
        return topicClient.liked(topicId, memberId, memberNickname, token, ipAddr);
    }
    
    //41:取消话题点赞
    @PutMapping(value="/{id}/like/cancel", consumes="application/x-www-form-urlencoded")
    public boolean unLiked(@PathVariable("id") long topicId,
                @RequestParam("member") long memberId,
                @RequestParam("nickname") String memberNickname,
                @RequestParam("token") String token,
                @RequestParam("ip") String ipAddr){
        return topicClient.unLiked(topicId, memberId, memberNickname, token, ipAddr);
    }
    
    //2:浏览话题
    @PutMapping(value="/{id}/browse", consumes="application/x-www-form-urlencoded")
    public ResponseEntity<String> browse(@PathVariable("id") long topicId,
                     @RequestParam("member") long memberId,
                     @RequestParam("nickname") String memberNickname,
                     @RequestParam("token") String token,
                     @RequestParam("ip") String ipAddr){
        try {
            return topicClient.browse(topicId, memberId, memberNickname, token, ipAddr);
        }catch (Exception e){
            logger.error("[nest-topic-browse]ResponseEntity exception:"+e.getMessage());
        }
        return ResponseEntity.badRequest().build();
    }
    
    //42:查看指定像册的图片
    @PostMapping(value="/album/picture/multi", consumes="application/x-www-form-urlencoded")
    public List<ThreadsAlbumPictureVo> getMultiAlbumPicture(@RequestParam("records") String albumIdString,
                                @RequestParam("scale") String scale,
                                @RequestParam("default_value") String defaultValue){
        return topicClient.getMultiAlbumPicture(albumIdString, scale, defaultValue);
    }
    
    //43:查看指定话题的像册
    @PostMapping(value="/album/multi", consumes="application/x-www-form-urlencoded")
    public List<ThreadsAlbumVo> getMultiCover(@RequestParam("records") String topicIdString,
                            @RequestParam("scale") String scale,
                            @RequestParam("default_value") String defaultValue){
        return topicClient.getMultiCover(topicIdString, scale, defaultValue);
    }
    
    //44:查看指定的像册
    @GetMapping("/album/{album}")
    public ResponseEntity<ThreadsAlbumVo> getAlbum(@PathVariable("album")long albumId){
        return topicClient.getAlbum(albumId);
    }
    
    //45:查看指定像册下的图片
    @PostMapping(value="/album/{album}/pictures", consumes="application/x-www-form-urlencoded")
    public List<ThreadsAlbumPictureRecordVo> getAlbumPictures(
            @PathVariable("album")long albumId,
            @RequestParam("scale") String scale,
            @RequestParam("default_value") String defaultValue,
            @RequestParam("size") int showSize){
        return topicClient.getAlbumPictures(albumId, scale, defaultValue, showSize);
    }
    
    //46:查看举报的类别
    @GetMapping("/report/category")
    public List<TopicCommonResponseVo> getReportType(){
        return topicClient.getReportType();
    }
    
    //47:根据标签查看相关的话题
    @GetMapping("/search/{word}/{page}/{number}")
    public EntityPageResult<ThreadsVo> getAllForTag(@PathVariable("word")String searchWord, @PathVariable("page")int page, @PathVariable("number")int pageSize){
        return topicClient.getAllForTag(searchWord, page, pageSize);
    }
    
    //48:查看指定话题相关的话题
    @GetMapping("/{id}/relate")
    public List<ThreadsTagRelateVo> getRelated(@PathVariable("id") long topicId, @RequestParam("size") int showSize){
        return topicClient.getRelated(topicId, showSize);
    }
    
    //49:查看指定话题的标签
    @GetMapping("/{id}/tag")
    public List<ThreadsTagVo> getTag(@PathVariable("id")long topicId){
        return topicClient.getTag(topicId);
    }
    
    //50:创建话题标签
    @PostMapping(value="/{id}/tag/create", consumes="application/x-www-form-urlencoded")
    public ResponseEntity<ThreadsTagVo> createTag(@PathVariable("id")long topicId,
                                 @RequestParam("tag")String tagNames,
                                 @RequestParam("member") long memberId,
                                 @RequestParam("nickname") String memberNickname,
                                 @RequestParam("token") String token,
                                 @RequestParam("ip") String ipAddr){
        try {
            ReplyVo rv = postsClient.getOneFloorContent(topicId).getBody();
            if (null == rv) {
                return ResponseEntity.status(400).contentType(MediaType.APPLICATION_JSON).body(ThreadsTagVo.empty(tagNames, topicId));
            }
            return topicClient.createTag(topicId, tagNames, rv.getContent(), memberId, memberNickname, token, ipAddr);
        }catch (Exception e){
            logger.error("[nest-topic-tag-create]ResponseEntity exception:"+e.getMessage());
        }
        return ResponseEntity.notFound().build();
    }
    
    //51:从话题中删除指定标签
    @PostMapping(value="/tag/{id}/remove", consumes="application/x-www-form-urlencoded")
    public boolean removeTag(@PathVariable("id")long tagId,
                   @RequestParam("threads")long topicId,
                   @RequestParam("member") long memberId,
                   @RequestParam("nickname") String memberNickname,
                   @RequestParam("token") String token,
                   @RequestParam("ip") String ipAddr){
        return topicClient.removeTag(tagId, topicId, memberId, memberNickname, token, ipAddr);
    }
    
    //52:分组统计出现频率最高的标签名称(TopicTag.names)
    @GetMapping("/tag/hot")
    public List<TopicCommonResponseVo> groupTagForNames(@RequestParam("size")int showSize){
        return topicClient.groupTagForNames(showSize);
    }

    private ThreadsUnionCheckVo queryTopicUnionOperate(long topicId, long memberId){
        ThreadsUnionCheckVo rs = new ThreadsUnionCheckVo();
        rs.setId(topicId);
        rs.setFavorite(topicClient.isFavorite(topicId, memberId)?"1":"0");
        rs.setLike(topicClient.isLike(topicId, memberId)?"1":"0");
        return rs;
    }
}